<?php

namespace app\admin\controller;

use app\admin\model\Admin;
use app\admin\model\MedicalStaff;
use app\admin\model\Room;
use Throwable;
use app\common\controller\Backend;
use think\route\RuleItem;

/**
 * 预约记录管理
 */
class Appointment extends Backend
{
    /**
     * Appointment模型对象
     * @var object
     * @phpstan-var \app\admin\model\Appointment
     */
    protected object $model;
    protected bool|string|int $dataLimit = false;
    protected bool $storeldAutoFill = true;

    protected array|string $preExcludeFields = ['id', 'admin_id', 'created_at', 'updated_at', 'external_id', 'source'];

    protected array $withJoinTable = ['room', 'medicalStaff', 'store', 'operator'];

    protected string|array $quickSearchField = ['id', 'room_id', 'medical_staff_id', 'patient_name', 'patient_phone'];

    public function initialize(): void
    {
        parent::initialize();
        $this->model = new \app\admin\model\Appointment();
    }


    /**
     * 查看
     * @throws Throwable
     */
    public function index(): void
    {
        // 如果是 select 则转发到 select 方法，若未重写该方法，其实还是继续执行 index
        if ($this->request->param('select')) {
            $this->select();
        }

        /**
         * 1. withJoin 不可使用 alias 方法设置表别名，别名将自动使用关联模型名称（小写下划线命名规则）
         * 2. 以下的别名设置了主表别名，同时便于拼接查询参数等
         * 3. paginate 数据集可使用链式操作 each(function($item, $key) {}) 遍历处理
         */
        list($where, $alias, $limit, $order) = $this->queryBuilder();
        $where[] = $this->buildStoreWhere('appointment.store_id');

        $res = $this->model
            ->withJoin($this->withJoinTable, $this->withJoinType)
            ->alias($alias)
            ->where($where)
            ->order($order)
            ->paginate($limit);
        $res->visible(['room' => ['name'], 'medicalStaff' => ['name'], 'store' => ['name'], 'operator' => ['username']]);

        // 为预约记录添加关联的养生项目数据
        $items = $res->items();
        $items = $this->为预约列表添加养生项目数据($items);

        $this->success('', [
            'list'   => $items,
            'total'  => $res->total(),
            'remark' => get_route_remark(),
        ]);
    }


    public function add(): void
    {
        $this->setAfterValidateFun(function ($newData): array {
            $this->拼接日期时间($newData);
            $this->处理养生项目关联($newData);
            $this->is存在预约记录($newData);
            return $newData;
        })->setAfterExecFun(function ($newData): array {
            // 新增成功后处理养生项目关联
            if (isset($newData['_health_item_ids'])) {
                $this->保存养生项目关联($this->model->id, $newData['_health_item_ids']);
            }
            return $newData;
        });

        parent::add();
    }

    public function edit(): void
    {
        $pk  = $this->model->getPk();
        $id  = $this->request->param($pk);
        $row = $this->model->find($id);
        if (!$row) {
            $this->error(__('Record not found'));
        }

        $dataLimitAdminIds = $this->getDataLimitAdminIds();
        if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) {
            $this->error(__('You have no permission'));
        }

        if ($this->request->isPost()) {
            $this->setAfterValidateFun(function ($newData, $oldData): array {
                $this->拼接日期时间($newData);
                $this->处理养生项目关联($newData, $oldData['id'] ?? null);
                $this->is存在预约记录($newData, $oldData['id'] ?? null);
                return $newData;
            })->setAfterExecFun(function ($newData, $oldData): array {
                // 编辑成功后处理养生项目关联
                if (isset($newData['_health_item_ids'])) {
                    $this->保存养生项目关联($oldData->id, $newData['_health_item_ids']);
                }
                return $newData;
            });

            parent::edit();
            return;
        }

        // GET请求：返回编辑数据，包含关联的养生项目
        $row = $this->model->with(['healthItems'])->find($id);

        // 转换养生项目数据为前端需要的格式
        if ($row->healthItems && count($row->healthItems) > 0) {
            $healthItems = [];
            foreach ($row->healthItems as $item) {
                $healthItems[] = [
                    'health_item_id' => $item->id,
                    'name' => $item->name,
                    'duration' => $item->duration,
                    'description' => $item->description ?? ''
                ];
            }
            $row->health_items = $healthItems;
        }

        $this->success('', [
            'row' => $row
        ]);
    }
    /**
     * 按两个维度,房间和治疗师进行展示，方便快捷预约
     * @throws Throwable
     */
    public function quick(): void
    {
        $查询日期 = date('Y-m-d H:i:s', time());
        // $传入的查看日期 = $this->request->param('appointment_date');
        // if ($传入的查看日期 && $传入的查看日期 != date('Y-m-d')) {
        //     $查询日期 = $传入的查看日期 . ' 00:00:00';
        // }
        // var_dump($传入的查看日期);
        if (!($store_id = request()->store_id)) {
            $this->error('请选择一个门店！');
        }
        $medical_role_id = env('MEDICAL_ROLE_ID', '');
        if (!$medical_role_id) {
            $this->error('医师角色ID未配置');
        }
        $查询字段 = [
            'r.id as room_id',
            'r.name as room_name',
            'ms.name as medical_staff_name',
            'ms.id as medical_staff_id',
            'a.id as appointment_id',
            'a.start_time',
            'a.end_time',
            'a.patient_name',
            'a.patient_gender',
            'a.patient_phone',
            'a.symptoms'
        ];
        //  
        $可预约房间列表 = Room::alias('r')
            ->field($查询字段)
            ->leftJoin('appointment a', "r.id = a.room_id AND a.store_id = " . $store_id . " AND a.end_time >= '" . $查询日期 . "' AND a.is_cancel = 0 ")
            // ->leftJoin('medical_staff_role', 'a.medical_staff_id = medical_staff_role.medical_staff_id and medical_staff_role.role_id = ' . $medical_role_id)
            ->leftJoin('medical_staff ms', 'a.medical_staff_id = ms.id')
            ->where([$this->buildStoreWhere('r.store_id')])
            // ->order('a.start_time', 'desc')
            ->select();
        $可预约治疗师列表 = MedicalStaff::alias('ms')
            ->field($查询字段)
            ->join('medical_staff_role', 'ms.id = medical_staff_role.medical_staff_id and medical_staff_role.role_id = ' . $medical_role_id)
            ->leftJoin('appointment a', "ms.id = a.medical_staff_id and a.store_id = " . $store_id . " AND a.end_time >= '" . $查询日期 . "' AND a.is_cancel = 0")
            ->leftJoin('room r', 'r.id = a.room_id')
            ->leftJoin('store_medical_staff_relation smsr', 'smsr.medical_staff_id = ms.id and smsr.store_id = ' . $store_id)
            ->where([$this->buildStoreWhere('smsr.store_id'),['medical_staff_role.role_id', '=', $medical_role_id]])
            // ->order('a.start_time', 'desc')
            ->select();

        // 为预约记录添加关联的养生项目数据
        $可预约房间列表 = $this->添加养生项目数据($可预约房间列表);
        $可预约治疗师列表 = $this->添加养生项目数据($可预约治疗师列表);

        $this->success('', [
            'rooms'   => $this->按字段分组数据($可预约房间列表, 'room_id'),
            'medicals'   => $this->按字段分组数据($可预约治疗师列表, 'medical_staff_id'),
            // 'medical_total'  => $可预约治疗师列表->total(),
            // 'end_time_diff'  => $end_time_diff,
            // 'appointment_date' => $传入的查看日期 ? $传入的查看日期 : date('Y-m-d', time()),
            'appointment_stats' => $this->按日期统计预约数($可预约房间列表)
        ]);
    }

    private function 按日期统计预约数($list)
    {
        $统计数据 = [];

        // 生成15天的日期数组
        for ($i = 0; $i < 10; $i++) {
            $date = date('Y-m-d', strtotime("+{$i} days"));
            $统计数据[$date] = 0;
        }

        // 遍历预约数据，累加每天的预约数量
        foreach ($list as $item) {
            if ($item->start_time) {
                $date = date('Y-m-d', strtotime($item->start_time));
                if (isset($统计数据[$date])) {
                    $统计数据[$date]++;
                }
            }
        }

        return $统计数据;
    }

    /**
     * 为预约记录添加关联的养生项目数据
     * @param array $appointmentList 预约记录列表
     * @return array
     */
    private function 添加养生项目数据($appointmentList)
    {
        if (empty($appointmentList)) {
            return $appointmentList;
        }

        // 收集所有预约ID
        $appointmentIds = [];
        foreach ($appointmentList as $item) {
            if ($item->appointment_id) {
                $appointmentIds[] = $item->appointment_id;
            }
        }

        if (empty($appointmentIds)) {
            return $appointmentList;
        }

        // 查询预约关联的养生项目
        $healthItemsRelations = \think\facade\Db::name('appointment_health_items_relation')
            ->alias('ahir')
            ->leftJoin('health_items hi', 'ahir.health_item_id = hi.id')
            ->field('ahir.appointment_id, hi.id as health_item_id, hi.name, hi.duration')
            ->whereIn('ahir.appointment_id', $appointmentIds)
            ->select()
            ->toArray();

        // 按预约ID分组养生项目数据
        $healthItemsByAppointment = [];
        foreach ($healthItemsRelations as $relation) {
            $appointmentId = $relation['appointment_id'];
            if (!isset($healthItemsByAppointment[$appointmentId])) {
                $healthItemsByAppointment[$appointmentId] = [];
            }
            $healthItemsByAppointment[$appointmentId][] = [
                'health_item_id' => $relation['health_item_id'],
                'name' => $relation['name'],
                'duration' => $relation['duration'],
            ];
        }

        // 为每条预约记录添加养生项目数据
        foreach ($appointmentList as &$item) {
            if ($item->appointment_id && isset($healthItemsByAppointment[$item->appointment_id])) {
                $item->health_items = $healthItemsByAppointment[$item->appointment_id];
            } else {
                $item->health_items = [];
            }
        }

        return $appointmentList;
    }

    /**
     * 为预约列表添加关联的养生项目数据（用于index方法）
     * @param array $appointmentList 预约记录列表
     * @return array
     */
    private function 为预约列表添加养生项目数据($appointmentList)
    {
        if (empty($appointmentList)) {
            return $appointmentList;
        }

        // 收集所有预约ID
        $appointmentIds = [];
        foreach ($appointmentList as $item) {
            if ($item->id) {
                $appointmentIds[] = $item->id;
            }
        }

        if (empty($appointmentIds)) {
            return $appointmentList;
        }

        // 查询预约关联的养生项目
        $healthItemsRelations = \think\facade\Db::name('appointment_health_items_relation')
            ->alias('ahir')
            ->leftJoin('health_items hi', 'ahir.health_item_id = hi.id')
            ->field('ahir.appointment_id, hi.id as health_item_id, hi.name, hi.duration, hi.description')
            ->whereIn('ahir.appointment_id', $appointmentIds)
            ->select()
            ->toArray();

        // 按预约ID分组养生项目数据
        $healthItemsByAppointment = [];
        foreach ($healthItemsRelations as $relation) {
            $appointmentId = $relation['appointment_id'];
            if (!isset($healthItemsByAppointment[$appointmentId])) {
                $healthItemsByAppointment[$appointmentId] = [];
            }
            $healthItemsByAppointment[$appointmentId][] = [
                'health_item_id' => $relation['health_item_id'],
                'name' => $relation['name'],
                'duration' => $relation['duration'],
                'description' => $relation['description']
            ];
        }

        // 为每条预约记录添加养生项目数据
        foreach ($appointmentList as &$item) {
            if ($item->id && isset($healthItemsByAppointment[$item->id])) {
                $item->health_items = $healthItemsByAppointment[$item->id];
            } else {
                $item->health_items = [];
            }
        }

        return $appointmentList;
    }

    /**
     * 根据某个字段值将数据分组
     * @param string $field 字段名
     * @return array
     */
    private function 按字段分组数据($list, $field)
    {
        $分组后数据 = [];
        foreach ($list as $item) {
            $分组后数据[$item->$field][] = $item;
        }
        if (count($分组后数据) === 0) return [];
        $重新组装成前端数据 = [];
        foreach ($分组后数据 as $key => $value) {
            $temp_data = isset($value[0]) ? $value[0] : [];
            if ($temp_data) {
                $temp_data  = $temp_data->toArray();
            }
            $temp_data['childrens']  = $value;
            $重新组装成前端数据[] = $temp_data;
        }
        $分组后数据 = null;
        return $重新组装成前端数据;
    }


    private function 拼接日期时间(&$data)
    {
        if (isset($data['appointment_date'])) {
            if (isset($data['start_time'])) {
                $data['start_time'] = $data['appointment_date'] . ' ' . $data['start_time'] . ':00';
            }
            // if (isset($data['end_time'])) {
            //     $data['end_time'] = $data['appointment_date'] . ' ' . $data['end_time'] . ':00';
            // }
        }
    }

    /**
     * 处理养生项目关联
     * @param array $data 预约数据
     * @param int|null $appointmentId 预约ID（编辑时使用）
     */
    private function 处理养生项目关联(&$data, $appointmentId = null)
    {
        // 如果有养生项目选择，处理项目关联和自动计算结束时间
        if (isset($data['healthItemSelections']) && is_array($data['healthItemSelections'])) {
            $healthItemIds = [];
            $totalDuration = 0;

            foreach ($data['healthItemSelections'] as $selection) {
                if (isset($selection['health_item_id']) && $selection['health_item_id']) {
                    $healthItemIds[] = $selection['health_item_id'];
                }
            }

            if (!empty($healthItemIds)) {
                // 获取项目总时长
                $healthItems = \app\admin\model\HealthItems::whereIn('id', $healthItemIds)->field('id,duration')->select();
                foreach ($healthItems as $item) {
                    $totalDuration += $item->duration;
                }

                // 如果选择了房间，额外增加15分钟
                if (isset($data['room_id']) && $data['room_id']) {
                    $totalDuration += 15;
                }

                // 自动计算结束时间
                if (isset($data['start_time']) && $totalDuration > 0) {
                    $startTime = strtotime($data['start_time']);
                    $endTime = $startTime + ($totalDuration * 60); // 转换为秒
                    $data['end_time'] = date('Y-m-d H:i:s', $endTime);
                }

                // 直接在控制器中处理养生项目关联，不再依赖模型事件
                $data['_health_item_ids'] = $healthItemIds;
            }
        }
    }

    /**
     * 处理养生项目关联数据库操作
     * @param int $appointmentId 预约ID
     * @param array $healthItemIds 养生项目ID数组
     */
    private function 保存养生项目关联($appointmentId, $healthItemIds)
    {
        if (!empty($healthItemIds)) {
            // 先删除旧的关联
            \think\facade\Db::name('appointment_health_items_relation')
                ->where('appointment_id', $appointmentId)
                ->delete();

            // 添加新的关联
            $relationData = [];
            foreach ($healthItemIds as $healthItemId) {
                $relationData[] = [
                    'appointment_id' => $appointmentId,
                    'health_item_id' => $healthItemId,
                ];
            }

            if (!empty($relationData)) {
                \think\facade\Db::name('appointment_health_items_relation')
                    ->insertAll($relationData);
            }
        }
    }
    /**
     * 检查治疗师在指定时间段是否已有预约
     * @param array $data 待检查的数据
     * @param int $id
     * @return void
     * @throws Throwable
     */
    public function is存在预约记录($data, $id = null)
    {
        if (!isset($data['medical_staff_id']) || !isset($data['start_time'])) {
            return;
        }
        // 检查治疗师在指定时间段是否已有预约
        $传入的治疗师id = $data['medical_staff_id'];
        $查询开始时间 = $data['start_time'];
        $查询结束时间 = $data['end_time'];
        $存在的预约记录 = $this->model
            ->where('medical_staff_id', $传入的治疗师id)
            ->where(function ($query) use ($查询开始时间, $查询结束时间) {
                // 检查时间段重叠的情况
                $query->where(function ($q) use ($查询开始时间) {
                    $q->where('start_time', '<=', $查询开始时间)
                        ->where('end_time', '>', $查询开始时间);
                })->whereOr(function ($q) use ($查询结束时间) {
                    $q->where('start_time', '<', $查询结束时间)
                        ->where('end_time', '>=', $查询结束时间);
                })->whereOr(function ($q) use ($查询开始时间, $查询结束时间) {
                    $q->where('start_time', '>=', $查询开始时间)
                        ->where('end_time', '<=', $查询结束时间);
                });
            })
            ->where('is_cancel', '=', 0) // 排除已取消的预约
            // 如果传入ID不为空,则排除当前记录
            ->when($id, function ($query) use ($id) {
                return $query->where('appointment.id', '<>', (int)$id);
            })
            ->withJoin(['room'])
            ->find();
        if ($存在的预约记录) {
            $房间名称 = $存在的预约记录->room->name ?? '未知房间';
            $开始时间 = date('H:i', strtotime($存在的预约记录->start_time));
            $结束时间 = date('H:i', strtotime($存在的预约记录->end_time));
            throw new \think\Exception("该治疗师在 {$开始时间} 至 {$结束时间} 时间段内已被预约在 \"{$房间名称}\"");
        }
    }

    /**
     * 取消预约
     */
    public function cancel(): void
    {
        $data = $this->request->post();
        if (!$data) {
            $this->error(__('Parameter %s can not be empty', ['']));
        }

        $id = $data['id'] ?? null;
        $cancel_reason = $data['cancel_reason'] ?? '';

        if (!$id) {
            $this->error('预约ID不能为空');
        }
        $appointment = $this->model->find($id);
        if (!$appointment) {
            $this->error('预约记录不存在');
        }

        // 检查预约是否已经取消
        if ($appointment->is_cancel == 1) {
            $this->error('该预约已经取消');
        }

        // 检查预约是否已经结束
        // var_dump($appointment->end_time,' ',time());
        if (strtotime($appointment->end_time) < time()) {
            $this->error('该预约已经结束，无法取消');
        }

        $result = false;
        $this->model->startTrans();
        try {
            $appointment->is_cancel = 1;
            $appointment->cancel_reason = $cancel_reason;
            // $appointment->cancel_time = date('Y-m-d H:i:s');
            $result = $appointment->save();
            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->error($e->getMessage());
        }

        $result !== false ? $this->success(__('Cancel successful')) : $this->error(__('Cancel failed'));
    }

    public function release(): void
    {
        $data = $this->request->post();
        if (!$data) {
            $this->error(__('Parameter %s can not be empty', ['']));
        }

        $id = $data['id'] ?? null;
        if (!$id) {
            $this->error('预约ID不能为空');
        }

        $appointment = $this->model->find($id);
        if (!$appointment) {
            $this->error('预约记录不存在');
        }

        $result = false;
        $this->model->startTrans();
        try {
            $appointment->end_time = date('Y-m-d H:i:s');
            $result = $appointment->save();
            $this->model->commit();
        } catch (Throwable $e) {
            $this->model->rollback();
            $this->error($e->getMessage());
        }

        $result !== false ? $this->success(__('Edit successful')) : $this->error(__('Edit failed'));
    }
}
